"""
Models for applications deploying to this platform.
"""

# from ..baseapp import db


import os
from typing import List, Literal, Optional, TypedDict

PROXY_TYPES = Literal["http-subroute", "http-dedicated-port"]


class AppPortConfig(TypedDict):
    tag: str
    port: int
    no_mapping_check: bool


class BaseDevEnvAppCfg:
    def __init__(
        self,
        name: str,
        description: str,
    ):
        self.name = name
        self.description = description

    def verify_other_ports(self, other_ports: dict[str, str]) -> tuple[bool, str]:
        """
        Verify that the ports are valid.
        """
        return True, ""

    def get_volumes(self, base_volume_path: str) -> List[str]:
        """
        Get the volumes for the application.
        """
        return [f"{base_volume_path}:{base_volume_path}"]

    def get_port(self) -> int:
        """
        Get the port for the application.
        """
        raise NotImplementedError

    def get_environments(self) -> dict[str, str]:
        """
        Get the environment variables for the application.
        """
        raise NotImplementedError

    def get_command(self, proxy_type: PROXY_TYPES, subroute: str = "") -> List[str]:
        """
        Get the command to run the application.
        """
        if proxy_type == "http-subroute":
            return self.get_command_on_subroutes(subroute)
        elif proxy_type == "http-dedicated-port":
            return self.get_command_on_dedicated_port()
        else:
            raise ValueError(f"Invalid proxy_type: {proxy_type}")

    def get_command_on_subroutes(self, subroute: str) -> List[str]:
        """
        Get the command to run the application with proxied on a specific nginx subroute.
        """
        raise NotImplementedError

    def get_command_on_dedicated_port(self) -> List[str]:
        """
        Get the command to run the application with proxied on a specific nginx port.
        """
        raise NotImplementedError

    def get_proxy_pass_url_subroute(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        raise NotImplementedError

    def get_proxy_pass_url_dedicated_port(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        raise NotImplementedError


class CodeServerAppCfg(BaseDevEnvAppCfg):
    def __init__(self):
        super().__init__("Code-Server", """Web-Based VSCode to code on your browser.""")

    def get_port(self) -> int:
        return 8080

    def get_environments(self) -> dict[str, str]:
        return {}

    def get_command_on_subroutes(self, subroute: str) -> Optional[List[str]]:
        return self.get_command_on_dedicated_port()

    def get_command_on_dedicated_port(self) -> Optional[List[str]]:
        return [
            "code-server",
            "--host",
            "0.0.0.0",
            "--auth",
            "none",
        ]

    def get_proxy_pass_url_subroute(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        return f"http://{host_machine_ip}:{container_port}/"

    def get_proxy_pass_url_dedicated_port(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        return f"http://{host_machine_ip}:{container_port}/"


class JupyterLabAppCfg(BaseDevEnvAppCfg):
    def __init__(self):
        super().__init__("Jupyter Lab", """A Jupyter Lab""")

    def get_environments(self) -> dict[str, str]:
        return {}

    def get_port(self) -> int:
        return 8888

    def get_command_on_subroutes(self, subroute: str) -> Optional[List[str]]:
        return [
            "jupyter",
            "lab",
            "--allow-root",
            "--no-browser",
            "--ip",
            "0.0.0.0",
            "--NotebookApp.token",
            "",
            "--NotebookApp.base_url",
            f"/{subroute}",
        ]

    def get_command_on_dedicated_port(self) -> Optional[List[str]]:
        return [
            "jupyter",
            "lab",
            "--allow-root",
            "--no-browser",
            "--ip",
            "0.0.0.0",
            "--NotebookApp.token",
            "",
        ]

    def get_proxy_pass_url_subroute(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        return f"http://{host_machine_ip}:{container_port}/{dev_env_name}/"

    def get_proxy_pass_url_dedicated_port(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        return f"http://{host_machine_ip}:{container_port}/"


class GenericWebAppCfg(BaseDevEnvAppCfg):
    def __init__(
        self,
        name: str,
        description: str,
        web_port: Optional[int] = None,
        other_ports: Optional[list[AppPortConfig]] = None,
        environments: Optional[dict[str, str]] = None,
        volumes: Optional[list[str]] = None,
    ):
        super().__init__(name, description)
        self.web_port = web_port if web_port else -1
        self.environments = environments if environments else {}
        self.volumes = volumes if volumes else []
        self.other_ports = other_ports if other_ports else []
        for i in range(len(self.volumes)):
            host_volume_relpath, container_volume_path = self.volumes[i].split(":")
            assert not host_volume_relpath.startswith(
                "/"
            ), f"{host_volume_relpath} should not start with `/`"
            # self.volumes[i] = f"{base_volume_path}/{host_volume_relpath}:{container_volume_path}"

    def verify_other_ports(self, other_ports: dict[str, str]) -> tuple[bool, str]:
        not_mapped_ports = []
        for other_port_config in self.other_ports:
            if not other_port_config.get("no_mapping_check", False):
                if str(other_port_config["port"]) not in other_ports:
                    not_mapped_ports.append(str(other_port_config["port"]))
        if len(not_mapped_ports) > 0:
            return (
                False,
                f"Ports {','.join(not_mapped_ports)} is not mapped to any container port. Please map it in the app config.",
            )
        return True, ""

    def get_volumes(self, base_volume_path: str) -> List[str]:
        if self.volumes:
            volumes = []
            for volume in self.volumes:
                host_volume_relpath, container_volume_path = volume.split(":")
                volumes.append(
                    f"{os.path.join(base_volume_path, host_volume_relpath)}:{container_volume_path}"
                )
            return volumes
        return super().get_volumes(base_volume_path)

    def get_port(self) -> int:
        """
        If port is <0, means that the app needs no web service.
        """
        return self.web_port

    def get_environments(self) -> dict[str, str]:
        return self.environments

    def get_command_on_subroutes(self, subroute: str) -> Optional[List[str]]:
        raise NotImplementedError()

    def get_command_on_dedicated_port(self) -> Optional[List[str]]:
        return []

    def get_proxy_pass_url_subroute(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        raise NotImplementedError()

    def get_proxy_pass_url_dedicated_port(
        self, host_machine_ip: str, container_port: int, dev_env_name: str
    ) -> str:
        return f"http://{host_machine_ip}:{container_port}/"
